module IPFS.Client.Command.Swarm (
 peers,
 Peer(..),
 Peers(..),
 PeerProtocol,
 localAddr,
 listenAddr,
 Addr,
 connect,
 disconnect
) where
import Data.Argonaut (class DecodeJson, Json, decodeJson, fromString, (.:))
import Data.Either (Either)
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Show (genericShow)
import Data.Maybe (Maybe(..))
import Data.Tuple.Nested ((/\))
import Effect.Aff (Aff)
import Foreign.Object as O
import IPFS.Client as C
import IPFS.Client.Command (Command, defCommand, genQSObject)
import IPFS.Client.Utils.SendRequest (execJsonCommand)
import Prelude (class Show, bind, pure, show, ($), (<$>))

data Addr = Addr {
  strings::Array String
}
derive instance genericAddr :: Generic Addr _
instance showAddr :: Show Addr where
  show s = genericShow s

instance addrDecodeJson :: DecodeJson Addr where
  decodeJson json = do
    djson::O.Object Json  <- decodeJson json
    strings               <- djson .: "Strings"
    pure $ Addr {strings:strings}

------------------------peers---------------------------------
data PeerProtocol = PeerProtocol {
  protocol::String
}

derive instance genericPeerProtocol :: Generic PeerProtocol _
instance showPeerProtocol :: Show PeerProtocol where
  show s = genericShow s

instance peerProtocolDecodeJson :: DecodeJson PeerProtocol where
  decodeJson json = do
    djson::O.Object Json <- decodeJson json
    protocol        <- djson .: "Protocol"
    pure $ PeerProtocol {protocol:protocol} 




data Peer = Peer {
 addr     ::String,
 peer     ::String,
 latency  ::String,
 muxer    ::String,
 direction::Int,
 streams  ::Maybe (Array PeerProtocol)
}

derive instance genericPeer :: Generic Peer _
instance showPeer :: Show Peer where
  show s = genericShow s

instance peerDecodeJson :: DecodeJson Peer where
  decodeJson json = do
    djson::O.Object Json                       <- decodeJson json
    addr                                       <- djson .: "Addr"
    peer                                       <- djson .: "Peer"
    latency                                    <- djson .: "Latency"
    muxer                                      <- djson .: "Muxer"
    direction                                  <- djson .: "Direction"
    streams::Maybe (Array PeerProtocol)        <- djson .: "Streams"
   
    pure $ Peer {addr:addr,peer:peer,latency:latency,muxer:muxer,direction:direction,streams:streams}

data Peers = Peers {
 peers::Array Peer
}

derive instance genericPeers :: Generic Peers _
instance showPeers :: Show Peers where
  show s = genericShow s

instance peersDecodeJson :: DecodeJson Peers where
  decodeJson json = do
   djson::O.Object Json <- decodeJson json
   peers_                <- djson .: "Peers"
   pure $ Peers {peers:peers_}

peersCommand::Maybe Boolean -> Maybe Boolean -> Maybe Boolean -> Maybe Boolean -> Command
peersCommand verbose streams latency direction = defCommand { 
    path = "swarm/peers", 
    args =  Nothing, 
    qs   =  genQSObject ["verbose" /\   (show <$> verbose),
                         "streams" /\   (show <$> streams),
                         "latency" /\   (show <$> latency),
                         "direction" /\ (show <$> direction)
                      ] O.empty
}

peers::C.Client -> Maybe Boolean -> Maybe Boolean -> Maybe Boolean -> Maybe Boolean -> Aff (Either String Peers)
peers client verbose streams latency direction = execJsonCommand client $ peersCommand verbose streams latency direction

------------------------addr/local---------------------------------


localAddrCommand::Command
localAddrCommand = defCommand {path = "swarm/addrs/local",args = Nothing,qs = O.empty}

localAddr::C.Client -> Aff (Either String Addr)
localAddr client = execJsonCommand client localAddrCommand

------------------------addr/listen---------------------------------
listenAddrCommand::Command
listenAddrCommand = defCommand {path = "swarm/addrs/listen",args = Nothing,qs = O.empty}

listenAddr::C.Client -> Aff (Either String Addr)
listenAddr client = execJsonCommand client listenAddrCommand

-----------------------connect---------------------------------------
connectCommand::String -> Command
connectCommand addr = defCommand {path = "swarm/connect",args = Just [fromString addr],qs = O.empty}

connect::C.Client -> String -> Aff (Either String Addr)
connect client  addr = execJsonCommand client $ connectCommand addr

-----------------------disconnect---------------------------------------
disconnectCommand::String -> Command
disconnectCommand addr = defCommand {path = "swarm/disconnect",args = Just [fromString addr],qs = O.empty}

disconnect::C.Client -> String -> Aff (Either String Addr)
disconnect client  addr = execJsonCommand client $ disconnectCommand addr